// exception.cc 
//	Entry point into the Nachos kernel from user programs.
//	There are two kinds of things that can cause control to
//	transfer back to here from user code:
//
//	syscall -- The user code explicitly requests to call a procedure
//	in the Nachos kernel.  Right now, the only function we support is
//	"Halt".
//
//	exceptions -- The user code does something that the CPU can't handle.
//	For instance, accessing memory that doesn't exist, arithmetic errors,
//	etc.  
//
//	Interrupts (which can also cause control to transfer from user
//	code into the Nachos kernel) are handled elsewhere.
//
// For now, this only handles the Halt() system call.
// Everything else core dumps.
//
// Copyright (c) 1992-1993 The Regents of the University of California.
// All rights reserved.  See copyright.h for copyright notice and limitation 
// of liability and disclaimer of warranty provisions.

#include "copyright.h"
#include "system.h"
#include "interrupt.h"
#include "syscall.h"
#include "processTable.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>

#ifdef NETWORK
#include "network.h"
#include "post.h"
#endif

using namespace std;


int copyin(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes from the current thread's virtual address vaddr.
    // Return the number of bytes so read, or -1 if an error occors.
    // Errors can generally mean a bad virtual address was passed in.
    bool result;
    int n=0;			// The number of bytes copied in
    int *paddr = new int;
	
    while ( n >= 0 && n < len) {
		result = machine->ReadMem( vaddr, 1, paddr );
		while(!result) // FALL 09 CHANGES
		{
   			result = machine->ReadMem( vaddr, 1, paddr ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
		}	
		
		buf[n++] = *paddr;
		
		if ( !result ) {
			//translation failed
			return -1;
		}
		
		vaddr++;
    }
	
    delete paddr;
    return len;
}

int copyout(unsigned int vaddr, int len, char *buf) {
    // Copy len bytes to the current thread's virtual address vaddr.
    // Return the number of bytes so written, or -1 if an error
    // occors.  Errors can generally mean a bad virtual address was
    // passed in.
    bool result;
    int n=0;			// The number of bytes copied in
	
    while ( n >= 0 && n < len) {
		// Note that we check every byte's address
		result = machine->WriteMem( vaddr, 1, (int)(buf[n++]) );
		
		if ( !result ) {
			//translation failed
			return -1;
		}
		
		vaddr++;
    }
	
    return n;
}

void Create_Syscall(unsigned int vaddr, int len) {
    // Create the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  No
    // way to return errors, though...
    char *buf = new char[len+1];	// Kernel buffer to put the name in
	
    if (!buf) return;
	
    if( copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Create\n");
		delete buf;
		return;
    }
	
    buf[len]='\0';
	
    fileSystem->Create(buf,0);
    delete[] buf;
    return;
}

int Open_Syscall(unsigned int vaddr, int len) {
    // Open the file with the name in the user buffer pointed to by
    // vaddr.  The file name is at most MAXFILENAME chars long.  If
    // the file is opened successfully, it is put in the address
    // space's file table and an id returned that can find the file
    // later.  If there are any errors, -1 is returned.
    char *buf = new char[len+1];	// Kernel buffer to put the name in
    OpenFile *f;			// The new open file
    int id;				// The openfile id
	
    if (!buf) {
		printf("%s","Can't allocate kernel buffer in Open\n");
		return -1;
    }
	
    if( copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Open\n");
		delete[] buf;
		return -1;
    }
	
    buf[len]='\0';
	
    f = fileSystem->Open(buf);
    delete[] buf;
	
    if ( f ) {
		if ((id = currentThread->space->fileTable.Put(f)) == -1 )
			delete f;
		return id;
    }
    else
		return -1;
}

void Write_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one. For disk files, the file is looked
    // up in the current address space's open file table and used as
    // the target of the write.
    
    char *buf;		// Kernel buffer for output
    OpenFile *f;	// Open file for output
	
    if ( id == ConsoleInput) return;
    
    if ( !(buf = new char[len]) ) {
		printf("%s","Error allocating kernel buffer for write!\n");
		return;
    } else {
        if ( copyin(vaddr,len,buf) == -1 ) {
			printf("%s","Bad pointer passed to to write: data not written\n");
			delete[] buf;
			return;
		}
    }
	
    if ( id == ConsoleOutput) {
		for (int ii=0; ii<len; ii++) {
			printf("%c",buf[ii]);
		}
		
    } else {
		if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
			f->Write(buf, len);
		} else {
			printf("%s","Bad OpenFileId passed to Write\n");
			len = -1;
		}
    }
	
    delete[] buf;
}

int Read_Syscall(unsigned int vaddr, int len, int id) {
    // Write the buffer to the given disk file.  If ConsoleOutput is
    // the fileID, data goes to the synchronized console instead.  If
    // a Write arrives for the synchronized Console, and no such
    // console exists, create one.    We reuse len as the number of bytes
    // read, which is an unnessecary savings of space.
    char *buf;		// Kernel buffer for input
    OpenFile *f;	// Open file for output
	
    if ( id == ConsoleOutput) return -1;
    
    if ( !(buf = new char[len]) ) {
		printf("%s","Error allocating kernel buffer in Read\n");
		return -1;
    }
	
    if ( id == ConsoleInput) {
		//Reading from the keyboard
		scanf("%s", buf);
		
		if ( copyout(vaddr, len, buf) == -1 ) {
			printf("%s","Bad pointer passed to Read: data not copied\n");
		}
    } else {
		if ( (f = (OpenFile *) currentThread->space->fileTable.Get(id)) ) {
			len = f->Read(buf, len);
			if ( len > 0 ) {
				//Read something from the file. Put into user's address space
				if ( copyout(vaddr, len, buf) == -1 ) {
					printf("%s","Bad pointer passed to Read: data not copied\n");
				}
			}
		} else {
			printf("%s","Bad OpenFileId passed to Read\n");
			len = -1;
		}
    }
	
    delete[] buf;
    return len;
}

void Close_Syscall(int fd) {
    // Close the file associated with id fd.  No error reporting.
    OpenFile *f = (OpenFile *) currentThread->space->fileTable.Remove(fd);
	
    if ( f ) {
		delete f;
    } else {
		printf("%s","Tried to close an unopen file\n");
    }
}

//-------------new system calls in Project 2----------------------
void Printf_Syscall(int vaddr, int vaddr2, int argc) {
	DEBUG('w', "in printf syscall\n");
	char *format = new char[1024];	// kernel buf for output
	int *args = new int [argc];
	int *charValue = new int;
	int *intValue = new int;
	bool result;
	char c;
	int n = 0;
	printLock->Acquire();
	DEBUG('w', "Get arguments...\n");
	while (n < argc) {
		result = machine->ReadMem( vaddr2, 4, intValue);
		while(!result) // FALL 09 CHANGES
		{
   			result = machine->ReadMem( vaddr2, 4, intValue ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
		}	
		
		args[n++] = *intValue;
		
		if ( !result ) {
			//translation failed
			printLock->Release();
			return;
		}
		
		vaddr2 += 4;
    	}
	DEBUG('w', "Get arguments\n");
	n=0;
	
	do {
		result = machine->ReadMem( vaddr, 1, charValue );
		while(!result) // FALL 09 CHANGES
		{
			result = machine->ReadMem( vaddr, 1, charValue ); // FALL 09 CHANGES: TO HANDLE PAGE FAULT IN THE ReadMem SYS CALL
		}
		c = *charValue;
		format[n++] = c;
		
		if ( !result ) {
			//translation failed
			printLock->Release();
			return;
		}
		
		vaddr ++;
	} while (c != '\0');
	
	int i = 0;
	int j = 0;
	while (format[i] != '\0') {
		if (format[i] != '%') {
			printf("%c", format[i]);
		}else {
			switch (format[++i]) {
				case 'd':
					if (j<argc) {
						printf("%d", args[j++]);
					}
					break;
				default:
					break;
			}
		}
		i++;
	}
	DEBUG('w', "Printf done\n");
	printLock->Release();
	delete [] format;
	delete [] args;
	delete charValue;
	delete intValue;
}


void exec_kernel_thread(int vaddr) {
	currentThread->space->InitRegisters();
	currentThread->space->RestoreState();
	machine->Run();
}

int Exec_Syscall(unsigned int vaddr, int len) {
	DEBUG('w', "in exec syscall\n");
	char *buf = new char[len+1];    // Kernel buffer to put the name in
	AddrSpace *space;       // The addrspace for the executable thread
    int spaceId;                // The addrspace id
	
    if (!buf) {
		printf("%s","Can't allocate kernel buffer in Open\n");
		return -1;
    }
	
    if (copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Open\n");
		delete[] buf;
		return -1;
    }
	buf[len]='\0';
	OpenFile *executable = fileSystem->Open(buf);
	if (executable == NULL) {
		printf("Unable to open file %s\n", buf);
		return -1;
	}
	delete[] buf;
	space = new AddrSpace(executable);      // construct a new address space for the process
	Thread *t = new Thread("syscall exec thread");
	t->space = space;
	ProcessTableEntry *entry = new ProcessTableEntry(space);    // construct an entry in the process table
	processTableLock->Acquire();
	spaceId = processTable->Put(entry);   // store the entry in the process table
	DEBUG('w', "Exec new process spaceId %d\n", spaceId);
	t->threadId = entry->CreateThread(t->space->GetPageNum()-1);
	processTableLock->Release();
	t->spaceId = spaceId;                           // record the spaceId in the addrspace
	//delete executable;
	t->Fork((VoidFunctionPtr)exec_kernel_thread, 0);
	return spaceId;
}

void fork_kernel_thread(unsigned int vaddr) {
	// Set values for user registers
	// Important ones
	// PCReg (vaddr), NextPCReg = PCReg +4, StackReg: a thread specific value
	// DEBUG('w', "before run\n");
	// Find extra pages for the new thread stack
	threadLock->Acquire();
	int stackNum = currentThread->space->AllocNewStack();
	if (stackNum < 0) {
		// no enough memory
		printf("there is not enough memory for the new thread stack\n");
		currentThread->Finish();
		return;
	}
	processTableLock->Acquire();
	ProcessTableEntry *entry = (ProcessTableEntry*) processTable->Get(currentThread->spaceId);    // get the entry from process table
	entry->SetStackInfo(currentThread->threadId, stackNum);    // increase the thread number in this address space
	processTableLock->Release();
	currentThread->space->RestoreState();
	currentThread->space->InitRegisters();
	machine->WriteRegister(PCReg, vaddr);
	machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
	threadLock->Release();
	machine->Run(); // switch to user mode
}

void Fork_Syscall(unsigned int vaddr) {
	DEBUG('w', "in fork syscall\n");
	
	Thread *t = new Thread("syscall fork thread");
	t->space = currentThread->space;
	t->spaceId = currentThread->spaceId;
	
	processTableLock->Acquire();
	ProcessTableEntry *entry = (ProcessTableEntry*) processTable->Get(currentThread->spaceId);    // get the entry from process table
	t->threadId = entry->CreateThread(0);    // increase the thread number in this address space
	processTableLock->Release();
	if (t->threadId < 0) {
		// Too many threads
		printf("The number of thread in this process reaches the max value, you cannot fork any more.\n");
		return;
	}
	t->Fork((VoidFunctionPtr)fork_kernel_thread, vaddr);
}

void Yield_Syscall() {
	// current thread yield the CPU to another runnable thread
	currentThread->Yield();
}

void Exit_Syscall(int status) {
	if (status != 0) {
		printf("Program exit at status %d\n", status);
	}
	processTableLock->Acquire();
	ProcessTableEntry *entry = (ProcessTableEntry *)processTable->Get(currentThread->spaceId);   // get the process entry in the process table
	DEBUG('w', "Exiting thread %d \n", currentThread->threadId);
	entry->ExitThread(currentThread->threadId);
	if (entry->HasNoThread()) {    // this thread is the last thread in the entry
		// Clean the locks and CVs
		lockTableLock->Acquire();
		for (int i=0; i<MAX_LOCKS; i++) {
			if (kernelLocks[i].addr == currentThread->space) {
				delete kernelLocks[i].lock;
				kernelLocks[i].lock = NULL;
				lockMap->Clear(i);
			}
		}
		lockTableLock->Release();
		conditionTableLock->Acquire();
		for (int i=0; i<MAX_CVS; i++) {
			if (kernelCVs[i].addr == currentThread->space) {
				delete kernelCVs[i].condition;
				kernelCVs[i].condition = NULL;
				conditionMap->Clear(i);
			}
		}
		conditionTableLock->Release();
		entry = (ProcessTableEntry *)processTable->Remove(currentThread->spaceId); // delete the entry and release address space
		delete entry;
		DEBUG('w', "process exit spaceId %d\n",currentThread->spaceId);
	}
	if (processTable->itemNum == 0) {
		interrupt->Halt();
	}
	processTableLock->Release();
	/*Thread *next = scheduler->FindNextToRun();
	if (next == NULL) {
		interrupt->Halt();
		return;
	}*/
	currentThread->Finish();
}

int CreateLock_Syscall(unsigned int vaddr, int len) {
	DEBUG('w', "Create Lock\n");
	char *buf = new char[len+1];	// Kernel buffer to put the name in
    Lock *lock;			// The new created lock
    int id;			// The lock id
	
    if (!buf) {
		printf("%s","Can't allocate kernel buffer in Open\n");
		return -1;
    }
	
    if (copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Open\n");
		delete[] buf;
		return -1;
    }
	buf[len]='\0';
	
	lock = new Lock(buf);
	delete[] buf;
	// Find a slot in the kernel lock table
	lockTableLock->Acquire();
	id = lockMap->Find();
	lockTableLock->Release();
	if (id < 0) {
		printf("Error: the number of locks reaches the maximum value\n");
		return -1;
	}
	kernelLocks[id].lock = lock;
	kernelLocks[id].isToBeDeleted = FALSE;
	kernelLocks[id].addr = currentThread->space;
	return id;
}

void DestroyLock_Syscall(int id) {
	DEBUG('w', "In destroy lock %d\n", id);
	if (id < 0) {
		//printf("Error: the lock id %d is invalid\n", id);
		return;
	}
	// validate the argument
	lockTableLock->Acquire();
	if (kernelLocks[id].addr != currentThread->space) {
		printf("Error: You are not allowed to delete the lock in other process\n");
		lockTableLock->Release();
		return;
	}
	if (kernelLocks[id].lock == NULL) {
		printf("Error: The lock has been deleted\n");
		lockTableLock->Release();
		return;
	}
	if (kernelLocks[id].lock->getStatus() == FREE) {
		// No threads are waiting
		// delete the lock
		Lock *lock = kernelLocks[id].lock;
		delete lock;
		kernelLocks[id].lock = NULL;
		kernelLocks[id].addr = NULL;
		lockMap->Clear(id);
	}else {
		// Some one is waiting
		// delete it later
		kernelLocks[id].isToBeDeleted = TRUE;
	}
	lockTableLock->Release();
}

void AcquireLock_Syscall(int id) {
	DEBUG('w', "Acquire lock %d\n", id);
	if (id < 0) {
		printf("Error: the lock id %d is invalid\n", id);
		return;
	}
	// validate the argument
	lockTableLock->Acquire();
	if (kernelLocks[id].addr != currentThread->space) {
		printf("Error: You are not allowed to acquire the lock in other process\n");
		lockTableLock->Release();
		return;
	}
	if (kernelLocks[id].lock == NULL) {
		printf("Error: The lock has been deleted\n");
		lockTableLock->Release();
		return;
	}
	lockTableLock->Release();
	/*processTableLock->Acquire();
	ProcessTableEntry *entry = (ProcessTableEntry *)processTable->Get(currentThread->spaceId);
	entry->activeNum --;
	entry->inactiveNum ++;
	processTableLock->Release();*/
	kernelLocks[id].lock->Acquire();
	/*processTableLock->Acquire();
	entry->activeNum ++;
	entry->inactiveNum --;
	processTableLock->Release();*/
}

void ReleaseLock_Syscall(int id) {
	DEBUG('w', "Release lock %d\n", id);
	if (id < 0) {
		printf("Error: the lock id %d is invalid\n", id);
		return;
	}
	// validate the argument
	lockTableLock->Acquire();
	if (kernelLocks[id].addr != currentThread->space) {
		printf("Error: You are not allowed to release the lock in other process\n");
		lockTableLock->Release();
		return;
	}
	if (kernelLocks[id].lock == NULL) {
		printf("Error: The lock has been deleted\n");
		lockTableLock->Release();
		return;
	}
	kernelLocks[id].lock->Release();
	if (kernelLocks[id].lock->getStatus() == FREE && kernelLocks[id].isToBeDeleted) {
		// No threads are waiting
		// delete the lock
		Lock *lock = kernelLocks[id].lock;
		delete lock;
		kernelLocks[id].lock = NULL;
		kernelLocks[id].addr = NULL;
		lockMap->Clear(id);
	}
	lockTableLock->Release();
}

int CreateCondition_Syscall(unsigned int vaddr, int len) {
	DEBUG('w', "Create Condition\n");
	char *buf = new char[len+1];	// Kernel buffer to put the name in
    Condition *condition;			// The new created condition
    int id;			// The condtion id
	
    if (!buf) {
		printf("%s","Can't allocate kernel buffer in Open\n");
		return -1;
    }
	
    if (copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Open\n");
		delete[] buf;
		return -1;
    }
	buf[len]='\0';
	
	condition = new Condition(buf);
	
	conditionTableLock->Acquire();
	id = conditionMap->Find();
	conditionTableLock->Release();
	if (id < 0) {
		printf("Error: the number of CVs reaches the maximum value\n");
		return -1;
	}
	kernelCVs[id].condition = condition;
	kernelCVs[id].addr = currentThread->space;
	kernelCVs[id].isToBeDeleted = FALSE;
	DEBUG('w', "Create Condition %s with id %d\n", condition->getName(), id);
	delete[] buf;
	return id;
}

void DestroyCondition_Syscall(int id) {
	DEBUG('w', "Destroy Condition\n");
	if (id < 0) {
		//printf("Error: the condition id %d is invalid\n", id);
		return;
	}
	// validate the argument
	conditionTableLock->Acquire();
	if (kernelCVs[id].addr != currentThread->space) {
		printf("Error: You are not allowed to delete the condition variable in other process\n");
		conditionTableLock->Release();
		return;
	}
	if (kernelCVs[id].condition == NULL) {
		printf("Error: The condition variable has been deleted\n");
		conditionTableLock->Release();
		return;
	}
	Condition *condition = kernelCVs[id].condition;
	if (condition->IsQueueEmpty()) {
		delete condition;
		kernelCVs[id].condition = NULL;
		kernelCVs[id].addr = NULL;
		conditionMap->Clear(id);
	}else {
		kernelCVs[id].isToBeDeleted = TRUE;
	}
	conditionTableLock->Release();
}

void WaitCondition_Syscall(int cid, int lid) {
	DEBUG('w', "Wait Condition %d, Lock %d\n", cid, lid);
	if (cid < 0) {
		printf("Error: the condition id %d is invalid\n", cid);
		return;
	}
	if (lid < 0) {
		printf("Error: the lock id %d is invalid\n", lid);
		return;
	}
	// validate the argument
	// validate lock
	Lock *lock;
	lockTableLock->Acquire();
	if (kernelLocks[lid].addr != currentThread->space) {
		printf("Error: You are not allowed to use the lock in other process\n");
		lockTableLock->Release();
		return;
	}
	if (kernelLocks[lid].lock == NULL) {
		printf("Error: The lock has been deleted\n");
		lockTableLock->Release();
		return;
	}
	lock = kernelLocks[lid].lock;
	conditionTableLock->Acquire();
	lockTableLock->Release();
	// validate condition variable
	if (kernelCVs[cid].addr != currentThread->space) {
		printf("Error: You are not allowed to wait on condition variable in other process\n");
		conditionTableLock->Release();
		return;
	}
	if (kernelCVs[cid].condition == NULL) {
		printf("Error: The condition variable has been deleted\n");
		conditionTableLock->Release();
		return;
	}
	Condition *condition = kernelCVs[cid].condition;
	conditionTableLock->Release();
	/*processTableLock->Acquire();
	ProcessTableEntry *entry = (ProcessTableEntry *)processTable->Get(currentThread->spaceId);
	entry->activeNum --;
	entry->inactiveNum ++;
	processTableLock->Release();*/
	condition->Wait(lock);
	/*processTableLock->Acquire();
	entry->activeNum ++;
	entry->inactiveNum --;
	processTableLock->Release();*/
}

void SignalCondition_Syscall(int cid, int lid) {
	DEBUG('w', "Signal Condition %d, Lock %d\n", cid, lid);	
	if (cid < 0) {
		printf("Error: the condition id %d is invalid\n", cid);
		return;
	}
	if (lid < 0) {
		printf("Error: the lock id %d is invalid\n", lid);
		return;
	}
	// validate the argument
	// validate lock
	Lock *lock;
	lockTableLock->Acquire();
	if (kernelLocks[lid].addr != currentThread->space) {
		printf("Error: You are not allowed to use the lock in other process\n");
		lockTableLock->Release();
		return;
	}
	if (kernelLocks[lid].lock == NULL) {
		printf("Error: The lock has been deleted\n");
		lockTableLock->Release();
		return;
	}
	lock = kernelLocks[lid].lock;
	conditionTableLock->Acquire();
	lockTableLock->Release();
	// validate condition variable
	if (kernelCVs[cid].addr != currentThread->space) {
		printf("Error: You are not allowed to signal on condition variable in other process\n");
		conditionTableLock->Release();
		return;
	}
	if (kernelCVs[cid].condition == NULL) {
		printf("Error: The condition variable has been deleted\n");
		conditionTableLock->Release();
		return;
	}
	Condition *condition = kernelCVs[cid].condition;
	condition->Signal(lock);
	if (condition->IsQueueEmpty() && kernelCVs[cid].isToBeDeleted) {
		delete condition;
		kernelCVs[cid].condition = NULL;
		kernelCVs[cid].addr = NULL;
		conditionMap->Clear(cid);
	}
	conditionTableLock->Release();
}

void BroadcastCondition_Syscall(int cid, int lid) {
	DEBUG('w', "Broadcast Condition %d, Lock %d\n", cid, lid);
	if (cid < 0) {
		printf("Error: the condition id %d is invalid\n", cid);
		return;
	}
	if (lid < 0) {
		printf("Error: the lock id %d is invalid\n", lid);
		return;
	}
	// validate the argument
	// validate lock
	Lock *lock;
	lockTableLock->Acquire();
	if (kernelLocks[lid].addr != currentThread->space) {
		printf("Error: You are not allowed to use the lock in other process\n");
		lockTableLock->Release();
		return;
	}
	if (kernelLocks[lid].lock == NULL) {
		printf("Error: The lock has been deleted\n");
		lockTableLock->Release();
		return;
	}
	lock = kernelLocks[lid].lock;
	conditionTableLock->Acquire();
	lockTableLock->Release();
	// validate condition variable
	if (kernelCVs[cid].addr != currentThread->space) {
		printf("Error: You are not allowed to broadcast on condition variable in other process\n");
		conditionTableLock->Release();
		return;
	}
	if (kernelCVs[cid].condition == NULL) {
		printf("Error: The condition variable has been deleted\n");
		conditionTableLock->Release();
		return;
	}
	Condition *condition = kernelCVs[cid].condition;
	condition->Broadcast(lock);
	if (kernelCVs[cid].isToBeDeleted) {
		delete condition;
		kernelCVs[cid].condition = NULL;
		kernelCVs[cid].addr = NULL;
		conditionMap->Clear(cid);
	}
	conditionTableLock->Release();
}

#ifdef NETWORK
int CreateServerLock_Syscall(unsigned int vaddr, int len) {
	printf("Create Server Lock\n");
	char *buf = new char[len+1];	// Kernel buffer to put the name in
    int id;			// The lock id
	
    if (!buf) {
		printf("%s","Can't allocate kernel buffer in Open\n");
		return -1;
    }
	
    if (copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Open\n");
		delete[] buf;
		return -1;
    }
	buf[len]='\0';
	PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[len+4];
  char buffer[MaxMailSize];
  string sentdata = "CL " + (string)buf;
  data = (char *)sentdata.c_str();
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data)+1;
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
  if(strlen(buffer)<5){
  	id = atoi(buffer);
  	printf("Get Lock %d in box %d\n", id, outMailHdr.from);
	}
	else{
		printf("%s\n", buffer);
	}
	delete[] buf;
	delete[] data;
	return id;
}

void DestroyServerLock_Syscall(int id) {
	DEBUG('w', "In destroy Server lock %d\n", id);
	if (id < 0) {
		//printf("Error: the lock id %d is invalid\n", id);
		return;
	}
	// validate the argument
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char iddata[25];   
  sprintf(iddata, "%d", id);
  string temp = (string)iddata;
  int len = strlen((char*)temp.c_str());
  char *data = new char[4+len];
  temp = "DL " + temp;
  data = (char *)temp.c_str();
  char buffer[MaxMailSize];
  data[3+len] = '\0';
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  printf("%s\n", buffer);
  fflush(stdout);
	delete[] data;
}

void AcquireServerLock_Syscall(int id) {
	DEBUG('w', "Acquire Server lock %d\n", id);
	if (id < 0) {
		printf("Error: the lock id %d is invalid\n", id);
		return;
	}
	PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char iddata[25];   
  sprintf(iddata, "%d", id);
  string temp = (string)iddata;
  int len = strlen((char*)temp.c_str());
  char *data = new char[4+len];
  temp = "AL " + temp;
  data = (char *)temp.c_str();
  char buffer[MaxMailSize];
  data[3+len] = '\0';
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
  if(strlen(buffer)<5){
  	id = atoi(buffer);
  	printf("Acquire Lock %d in box %d\n", id, outMailHdr.from);
	}
	else{
		printf("%s\n", buffer);
	}
	delete[] data;
}

void ReleaseServerLock_Syscall(int id) {
	DEBUG('w', "Release Server lock %d\n", id);
	if (id < 0) {
		printf("Error: the lock id %d is invalid\n", id);
		return;
	}
	// validate the argument
	PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char iddata[25];   
  sprintf(iddata, "%d", id);
  string temp = (string)iddata;
  int len = strlen((char*)temp.c_str());
  char *data = new char[4+len];
  temp = "RL " + temp;
  data = (char *)temp.c_str();
  char buffer[MaxMailSize];
  data[3+len] = '\0';
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
  printf("%s\n", buffer);
	delete[] data;
}

int CreateServerCondition_Syscall(unsigned int vaddr, int len) {
	printf("Create Server CV\n");
	char *buf = new char[len+1];	// Kernel buffer to put the name in
    int id;			// The condtion id
	
    if (!buf) {
		printf("%s","Can't allocate kernel buffer in Open\n");
		return -1;
    }
	
    if (copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Open\n");
		delete[] buf;
		return -1;
    }
	buf[len]='\0';
	
	PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char *data = new char[len+4];
  char buffer[MaxMailSize];
  string sentdata = "CC " + (string)buf;
  data = (char *)sentdata.c_str();
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data)+1;
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
  if(strlen(buffer)<5){
  	id = atoi(buffer);
  	printf("Create CV %d in box %d\n", id, outMailHdr.from);
	}
	else{
		printf("%s\n", buffer);
	}
	delete[] buf;
	delete[] data;
	return id;
}

void DestroyServerCondition_Syscall(int id) {
	DEBUG('w', "Destroy Server Condition\n");
	if (id < 0) {
		//printf("Error: the condition id %d is invalid\n", id);
		return;
	}
	// validate the argument
	PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char iddata[25];   
  sprintf(iddata, "%d", id);
  string temp = (string)iddata;
  int len = strlen((char*)temp.c_str());
  char *data = new char[4+len];
  temp = "DC " + temp;
  data = (char *)temp.c_str();
  char buffer[MaxMailSize];
  data[3+len] = '\0';
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
	printf("%s\n", buffer);
	delete[] data;
}

void WaitServerCondition_Syscall(int cid, int lid) {
	int id;
	DEBUG('w', "Wait Server Condition %d, Lock %d\n", cid, lid);
	if (cid < 0) {
		printf("Error: the condition id %d is invalid\n", cid);
		return;
	}
	if (lid < 0) {
		printf("Error: the lock id %d is invalid\n", lid);
		return;
	}
	// validate the argument
	// validate lock
	PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char ciddata[25]; 
  char liddata[25];   
  sprintf(ciddata, "%d", cid);  
  sprintf(liddata, "%d", lid);
  string temp1 = (string)ciddata;
  string temp2 = (string)liddata;
  int len1 = strlen((char*)temp1.c_str());
  int len2 = strlen((char*)temp2.c_str());
  char *data = new char[5+len1+len2];
  temp1 = "WC " + temp1 + " " +temp2;
  data = (char *)temp1.c_str();
  char buffer[MaxMailSize];
  data[4+len1+len2] = '\0';
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
  printf("%s\n", buffer);
  for(int i=0; i<MaxMailSize; i++){
			buffer[i] = '\0';
	}
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
  if(strlen(buffer)<5){
  	id = atoi(buffer);
  	printf("Acquire Lock %d again after waiting in box %d\n", id, outMailHdr.from);
	}
	else{
		printf("%s\n", buffer);
	}
	delete[] data;
}

void SignalServerCondition_Syscall(int cid, int lid) {
	int id;
	DEBUG('w', "Signal Server Condition %d, Lock %d\n", cid, lid);	
	if (cid < 0) {
		printf("Error: the condition id %d is invalid\n", cid);
		return;
	}
	if (lid < 0) {
		printf("Error: the lock id %d is invalid\n", lid);
		return;
	}
	// validate the argument
	// validate lock
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char ciddata[25]; 
  char liddata[25];   
  sprintf(ciddata, "%d", cid);  
  sprintf(liddata, "%d", lid);
  string temp1 = (string)ciddata;
  string temp2 = (string)liddata;
  int len1 = strlen((char*)temp1.c_str());
  int len2 = strlen((char*)temp2.c_str());
  char *data = new char[5+len1+len2];
  temp1 = "SC " + temp1 + " " +temp2;
  data = (char *)temp1.c_str();
  char buffer[MaxMailSize];
  data[4+len1+len2] = '\0';
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
  printf("%s\n", buffer);
	delete[] data;
}

void BroadcastServerCondition_Syscall(int cid, int lid) {
	DEBUG('w', "Broadcast Server Condition %d, Lock %d\n", cid, lid);
	if (cid < 0) {
		printf("Error: the condition id %d is invalid\n", cid);
		return;
	}
	if (lid < 0) {
		printf("Error: the lock id %d is invalid\n", lid);
		return;
	}
	// validate the argument
	// validate lock
	PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char ciddata[25]; 
  char liddata[25];   
  sprintf(ciddata, "%d", cid);  
  sprintf(liddata, "%d", lid);
  string temp1 = (string)ciddata;
  string temp2 = (string)liddata;
  int len1 = strlen((char*)temp1.c_str());
  int len2 = strlen((char*)temp2.c_str());
  char *data = new char[5+len1+len2];
  temp1 = "BC " + temp1 + " " +temp2;
  data = (char *)temp1.c_str();
  char buffer[MaxMailSize];
  data[4+len1+len2] = '\0';
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
  printf("%s\n", buffer);
	delete[] data;
}

int CreateMV_Syscall(unsigned int vaddr, int len, int arrsize) {
	DEBUG('w', "Create MV\n");
	char *buf = new char[len+1];	// Kernel buffer to put the name in
    Lock *lock;			// The new created lock
    int id;			// The lock id
	
    if (!buf) {
		printf("%s","Can't allocate kernel buffer in Open\n");
		return -1;
    }
	
    if (copyin(vaddr,len,buf) == -1 ) {
		printf("%s","Bad pointer passed to Open\n");
		delete[] buf;
		return -1;
    }
	buf[len]='\0';
	
	PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char sizedata[25];   
  sprintf(sizedata, "%d", arrsize);
  string arr = (string)sizedata;
  char *data = new char[len+5+strlen(sizedata)];
  char buffer[MaxMailSize];
  string sentdata = "CM " + arr + " " + (string)buf;
  data = (char *)sentdata.c_str();
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
  if(strlen(buffer)<5){
  	id = atoi(buffer);
  	printf("Acquire MV %d in box %d\n", id, outMailHdr.from);
	}
	else{
		printf("%s\n", buffer);
	}
	delete[] buf;
	delete[] data;
	return id;
}

void DestroyMV_Syscall(int id) {
	DEBUG('w', "destroy MV %d\n", id);
	if (id < 0) {
		//printf("Error: the lock id %d is invalid\n", id);
		return;
	}
	// validate the argument
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char iddata[25];   
  sprintf(iddata, "%d", id);
  string temp = (string)iddata;
  int len = strlen((char*)temp.c_str());
  char *data = new char[4+len];
  temp = "DM " + temp;
  data = (char *)temp.c_str();
  char buffer[MaxMailSize];
  data[3+len] = '\0';
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
	printf("%s\n", buffer);
	delete[] data;
}

int GetMVValue_Syscall(int id, int slot) {
	int id2;
	DEBUG('w', "Get MV %d\n", id);
	if (id < 0) {
		//printf("Error: the lock id %d is invalid\n", id);
		return -1;
	}
	if (slot < 0) {
		//printf("Error: the lock id %d is invalid\n", id);
		return -1;
	}
	// validate the argument
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char iddata[25]; 
  char slotdata[25];    
  sprintf(iddata, "%d", id);
  sprintf(slotdata, "%d", slot);
  string temp1 = (string)iddata;
  string temp2 = (string)slotdata;
  int len1 = strlen((char*)temp1.c_str());
  int len2 = strlen((char*)temp2.c_str());
  char *data = new char[5+len1+len2];
  temp1 = "GM " + temp1 + " " +temp2;
  data = (char *)temp1.c_str();
  char buffer[MaxMailSize];
  data[4+len1+len2] = '\0';
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  //fflush(stdout);
  if(strlen(buffer)<5){
  	id2 = atoi(buffer);
  	printf("Get value %d in slot %d of MV %d in box %d\n", id2, slot, id, outMailHdr.from);
	}
	else{
		printf("%s\n", buffer);
	}
	delete[] data;
	return id;
}

void SetMVValue_Syscall(int id, int slot,int value) {
	DEBUG('w', "Get MV %d\n", id);
	if (id < 0) {
		//printf("Error: the lock id %d is invalid\n", id);
		return;
	}
	if (slot < 0) {
		//printf("Error: the lock id %d is invalid\n", id);
		return;
	}
	if (value < 0) {
		//printf("Error: the lock id %d is invalid\n", id);
		return;
	}
	// validate the argument
  PacketHeader outPktHdr, inPktHdr;
  MailHeader outMailHdr, inMailHdr;
  char iddata[25];  
  char slotdata[25]; 
  char valuedata[25]; 
  sprintf(iddata, "%d", id);
  sprintf(slotdata, "%d", slot);
  sprintf(valuedata, "%d", value);
  string temp1 = (string)iddata;
  string temp2 = (string)slotdata;
  string temp3 = (string)valuedata;
  int len1 = strlen((char*)temp1.c_str());
  int len2 = strlen((char*)temp2.c_str());
  int len3 = strlen((char*)temp3.c_str());
  char *data = new char[6+len1+len2+len3];
  temp1 = "SM " + temp1 + " " +temp2 + " " + temp3;
  data = (char *)temp1.c_str();
  char buffer[MaxMailSize];
  data[5+len1+len2+len3] = '\0';
  // construct packet, mail header for original message
  // To: destination machine, mailbox 0
  // From: our machine, reply to: mailbox 1
  outPktHdr.to = 0;		
  outMailHdr.to = 0;
  outMailHdr.from = 1;
  outMailHdr.length = strlen(data);
  
  bool success = postOffice->Send(outPktHdr, outMailHdr, data); 

    if ( !success ) {
      printf("The postOffice Send failed. You must not have the other Nachos running. Terminating Nachos.\n");
      interrupt->Halt();
    }
  
  // Wait for the first message from the other machine
  postOffice->Receive(1, &inPktHdr, &inMailHdr, buffer);
  //printf("Got \"%s\" from %d, box %d\n",buffer,inPktHdr.from,inMailHdr.from);
  fflush(stdout);
	printf("%s\n", buffer);
	delete[] data;
}
#endif

void PageFaultHandler(unsigned int vaddr) {
	//DEBUG('z', "Handling Page Fault\n");
	// Disable the interrupts
	IntStatus old = interrupt->SetLevel(IntOff);
	int vpn = vaddr/PageSize;
	int ppn = -1;
	int tlbIndex = -1;
	// Find an invalid slot
	for (int i=0; i<TLBSize; i++) {
		if(!machine->tlb[i].valid) {
			tlbIndex = i;
			break;
		}
	}
	
	if(tlbIndex < 0) {
		// No invalid slot, replace a valid slot
		tlbIndex = currentTLB;
		currentTLB = (currentTLB+1)%TLBSize;
		if(machine->tlb[tlbIndex].dirty) {
			// propagate the dirty bit
			ipt[machine->tlb[tlbIndex].physicalPage].dirty = machine->tlb[tlbIndex].dirty;
			ipt[machine->tlb[tlbIndex].physicalPage].use = machine->tlb[tlbIndex].use;
		}
	}
	
	// Find the page in IPT
	for (int i=0; i<NumPhysPages; i++) {
		if(ipt[i].valid && ipt[i].virtualPage == vpn && ipt[i].processId == (int)currentThread->space) {
			// Got a IPT hit
			ppn = i;
			break;
		}
	}
	
	if(ppn < 0) {
		// Handle the IPT miss
		ppn = currentThread->space->HandleIPTMiss(vpn);
	}
	
	// Copy the page to TLB
	//DEBUG('z', "Copy the page to TLB\n");
	machine->tlb[tlbIndex].valid = ipt[ppn].valid;
	machine->tlb[tlbIndex].virtualPage = ipt[ppn].virtualPage;
	machine->tlb[tlbIndex].physicalPage = ipt[ppn].physicalPage;
	machine->tlb[tlbIndex].readOnly = ipt[ppn].readOnly;
	machine->tlb[tlbIndex].use = ipt[ppn].use;
	machine->tlb[tlbIndex].dirty = ipt[ppn].dirty;
	
	// Restore interrupts
	interrupt->SetLevel(old);
}

void ExceptionHandler(ExceptionType which) {
    int type = machine->ReadRegister(2); // Which syscall?
    int rv=0; 	// the return value from a syscall
	
    if ( which == SyscallException ) {
		switch (type) {
			default:
				DEBUG('a', "Unknown syscall - shutting down.\n");
			case SC_Halt:
				DEBUG('a', "Shutdown, initiated by user program.\n");
				interrupt->Halt();
				break;
			case SC_Create:
				DEBUG('a', "Create syscall.\n");
				Create_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_Open:
				DEBUG('a', "Open syscall.\n");
				rv = Open_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_Write:
				DEBUG('a', "Write syscall.\n");
				Write_Syscall(machine->ReadRegister(4),
							  machine->ReadRegister(5),
							  machine->ReadRegister(6));
				break;
			case SC_Read:
				DEBUG('a', "Read syscall.\n");
				rv = Read_Syscall(machine->ReadRegister(4),
								  machine->ReadRegister(5),
								  machine->ReadRegister(6));
				break;
			case SC_Close:
				DEBUG('a', "Close syscall.\n");
				Close_Syscall(machine->ReadRegister(4));
				break;
				
				// new system calls in Project 2
			case SC_Yield:
				DEBUG('a', "Yield syscall.\n");
				Yield_Syscall();
				break;
			case SC_Exit:
				DEBUG('a', "Exit syscall.\n");
				Exit_Syscall(machine->ReadRegister(4));
				break;
			case SC_Fork:
				DEBUG('a', "Fork syscall.\n");
				Fork_Syscall(machine->ReadRegister(4));
				break;
			case SC_Exec:
				DEBUG('a', "Exec syscall.\n");
				Exec_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_CreateLock:
				DEBUG('a', "CreateLock syscall.\n");
				rv = CreateLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_DestroyLock:
				DEBUG('a', "DestroyLock syscall.\n");
				DestroyLock_Syscall(machine->ReadRegister(4));
				break;
			case SC_AcquireLock:
				DEBUG('a', "AcquireLock syscall.\n");
				AcquireLock_Syscall(machine->ReadRegister(4));
				break;
			case SC_ReleaseLock:
				DEBUG('a', "ReleaseLock syscall.\n");
				ReleaseLock_Syscall(machine->ReadRegister(4));
				break;
			case SC_CreateCondition:
				DEBUG('a', "CreateCondition syscall.\n");
				rv = CreateCondition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_DestroyCondition:
				DEBUG('a', "DestroyCondition syscall.\n");
				DestroyCondition_Syscall(machine->ReadRegister(4));
				break;
			case SC_WaitCondition:
				DEBUG('a', "WaitCondition syscall.\n");
				WaitCondition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_SignalCondition:
				DEBUG('a', "SignalCondition syscall.\n");
				SignalCondition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_BroadcastCondition:
				DEBUG('a', "BroadcastCondition syscall.\n");
				BroadcastCondition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
			break;
			case SC_Printf:
				DEBUG('a', "Printf syscall.\n");
				Printf_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
				break;
#ifdef NETWORK
			case SC_CreateServerLock:
				DEBUG('a', "CreateServerLock syscall.\n");
				rv = CreateServerLock_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_DestroyServerLock:
				DEBUG('a', "DestroyServerLock syscall.\n");
				DestroyServerLock_Syscall(machine->ReadRegister(4));
				break;
			case SC_AcquireServerLock:
				DEBUG('a', "AcquireServerLock syscall.\n");
				AcquireServerLock_Syscall(machine->ReadRegister(4));
				break;
			case SC_ReleaseServerLock:
				DEBUG('a', "ReleaseServerLock syscall.\n");
				ReleaseServerLock_Syscall(machine->ReadRegister(4));
				break;
			case SC_CreateServerCondition:
				DEBUG('a', "CreateServerCondition syscall.\n");
				rv = CreateServerCondition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_DestroyServerCondition:
				DEBUG('a', "DestroyServerCondition syscall.\n");
				DestroyServerCondition_Syscall(machine->ReadRegister(4));
				break;
			case SC_WaitServerCondition:
				DEBUG('a', "WaitServerCondition syscall.\n");
				WaitServerCondition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_SignalServerCondition:
				DEBUG('a', "SignalServerCondition syscall.\n");
				SignalServerCondition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_BroadcastServerCondition:
				DEBUG('a', "BroadcastServerCondition syscall.\n");
				BroadcastServerCondition_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_CreateMV:
				DEBUG('a', "CreateMV syscall.\n");
				rv = CreateMV_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
				break;
			case SC_DestroyMV:
				DEBUG('a', "DestroyMV syscall.\n");
				DestroyMV_Syscall(machine->ReadRegister(4));
				break;
			case SC_GetMVValue:
				DEBUG('a', "GetMVValue syscall.\n");
				rv = GetMVValue_Syscall(machine->ReadRegister(4), machine->ReadRegister(5));
				break;
			case SC_SetMVValue:
				DEBUG('a', "SetMVValue syscall.\n");
				SetMVValue_Syscall(machine->ReadRegister(4), machine->ReadRegister(5), machine->ReadRegister(6));
				break;
#endif
		}
		
		// Put in the return value and increment the PC
		machine->WriteRegister(2,rv);
		machine->WriteRegister(PrevPCReg,machine->ReadRegister(PCReg));
		machine->WriteRegister(PCReg,machine->ReadRegister(NextPCReg));
		machine->WriteRegister(NextPCReg,machine->ReadRegister(PCReg)+4);
		return;
    } else if (which == PageFaultException) {
		PageFaultHandler(machine->ReadRegister(BadVAddrReg));
		return;
	}else {
		cout<<"Unexpected user mode exception - which:"<<which<<"  type:"<< type<<endl;
		interrupt->Halt();
    }
}
